#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/module.h>
#include <linux/crypto.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/namei.h>
#include <linux/dirent.h>
#include <linux/kobject.h>
#include <linux/ip.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/skbuff.h>
#include <linux/if_pppox.h>
#include <linux/list.h>
#include <linux/ptrace.h>
#include <linux/spinlock.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <net/tcp.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>

static struct socket *l2tp_sock = NULL;

static int dump_buf(unsigned char *buf, int len)
{
    unsigned char *p;
    int i;
    for (p = buf, i = 1; i <= len; ++i, ++p) {
        if (i % 0x10 == 0 || i == len)
            printk(KERN_DEBUG "%02x\n", *p);            
        else
            printk(KERN_DEBUG "%02x ", *p);
    }
    return (0);
}

#ifdef TEST_UDP_RECV
typedef int (*udp_rcv_hook)(struct sk_buff *skb);
extern udp_rcv_hook test_udp_rcv_hook;
static int my_udp_rcv(struct sk_buff *skb)
{
    printk(KERN_DEBUG "enter %s\n", __FUNCTION__);    
    return (0);
}
#endif

static int l2tp_send(char *buff, size_t len)
{
	int err;
    struct msghdr msg;
	struct iovec iov;

	iov.iov_base = buff;
	iov.iov_len = len;
	msg.msg_name = NULL;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = NULL;
	msg.msg_controllen = 0;
	msg.msg_namelen = 0;
/*
    if (l2tp_sock->file->f_flags & O_NONBLOCK)
		flags |= MSG_DONTWAIT;
*/
	msg.msg_flags = O_RDWR;
	err = sock_sendmsg(l2tp_sock, &msg, len);
    return err;
/*
 *	err = ip_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen, 
			sizeof(struct udphdr), &ipc, rt, 
			corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);

	if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) {			
 *  		if (csum_partial_copy_fromiovecend(to, iov, offset, len, &csum) < 0) {
 */ 

}

static char buf[1600];

int testudp_init(void)
{
    int error;
    struct sockaddr_in addr;

    printk(KERN_DEBUG "%s init\n", __FUNCTION__);

    for (error = 0; error < sizeof(buf); ++error) {
        buf[error] = (error % 20) + 'a';
    }
    
	if ((error = sock_create_kern(PF_INET, SOCK_DGRAM, 0, &l2tp_sock)) < 0)
	{
		printk(KERN_DEBUG "socket error %d\n", -error);
		return error;
	}
	l2tp_sock->sk->sk_reuse = 1;
	l2tp_sock->sk->sk_allocation = GFP_ATOMIC;

    
    memset(&addr, 0, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(1701);    
/*    ret = inet_aton(argv[1], &dst.sin_addr);; */
    addr.sin_addr.s_addr = htonl(0xc0a80101);  /* 192.168.1.1 */

    error = l2tp_sock->ops->connect(l2tp_sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in),
				 0);
    if (error < 0)
	{
		printk(KERN_DEBUG "connect error %d\n", -error);
        goto fail;
	}

    error = l2tp_send(buf, 1600);
    printk(KERN_DEBUG "send ret %d\n", error);
    
#ifdef TEST_UDP_RECV    
    test_udp_rcv_hook = my_udp_rcv;
#endif    
    return 0;
fail:
    if (l2tp_sock) {
        l2tp_sock->ops->release(l2tp_sock);
        l2tp_sock = NULL;
    }
    return error;
}

void testudp_exit(void)
{
	printk(KERN_DEBUG "%s exit\n", __FUNCTION__);
    if (l2tp_sock) {
        l2tp_sock->ops->release(l2tp_sock);
        l2tp_sock = NULL;
    }
#ifdef TEST_UDP_RECV
    test_udp_rcv_hook = NULL;
#endif
}

module_init(testudp_init);
module_exit(testudp_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("tpl.a1406@gmail.com");

